package com.magic.platform.framework.util;

import com.google.common.collect.Lists;
import com.magic.platform.core.model.MongoPageResult;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.bson.types.ObjectId;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.StringUtils;

/**
 * @Author: zyn
 * @Description:
 * @Date: Created in 2018-10-16 13:13
 * @Modified By:
 */
//@Component
@ConditionalOnExpression("${framework.config.mongo.enable: false}")
public class MongoPageHelper {

  public static final int FIRST_PAGE_NUM = 1;
  public static final String ID = "_id";

  private final MongoTemplate mongoTemplate;

  public MongoPageHelper(MongoTemplate mongoTemplate) {
    this.mongoTemplate = mongoTemplate;
  }

  /**
   * 分页查询，直接返回集合类型的结果.
   */
  public <T> MongoPageResult<T> pageQuery(Query query, Class<T> entityClass,  Integer pageNum, Integer pageSize) {
    return pageQuery(query, entityClass, Function.identity(), pageNum, pageSize, null);
  }

  /**
   * 分页查询，不考虑条件分页，直接使用skip-limit来分页
   */
  public <T, R> MongoPageResult<R> pageQuery(Query query, Class<T> entityClass, Function<T, R> mapper,  Integer pageNum, Integer pageSize) {
    return pageQuery(query, entityClass, mapper, pageNum, pageSize, null);
  }

  /**
   *
   * @param query Mongo Query对象，构造你自己的查询条件
   * @param entityClass Mongo collection 定义的 entity class，用来确定查询哪个集合
   * @param mapper 映射器，你从db查出来的list的元素类型是entityClass, 如果你想要转换成另一个对象，比如去掉敏感字段等，可以使用mapper来决定如何转换
   * @param pageSize 分页的大小
   * @param pageNum 当前页
   * @param lastId 条件分页参数, 区别于skip-limit，采用find(_id>lastId).limit分页.
   *      * 如果不跳页，像朋友圈，微博这样下拉刷新的分页需求，需要传递上一页的最后一条记录的ObjectId。 如果是null，则返回pageNum那一页
   * @param <T> collection定义的class类型
   * @param <R> 最终返回时，展现给页面时的一条记录的类型
   * @return 一个封装page信息的对象
   */
  public <T, R> MongoPageResult<R> pageQuery(Query query, Class<T> entityClass, Function<T, R> mapper, Integer pageNum, Integer pageSize, String lastId) {
    //分页逻辑
    long total = mongoTemplate.count(query, entityClass);
    final Integer pages = (int) Math.ceil(total / (double) pageSize);
    if (pageNum <= 0 || pageNum > pages) {
      pageNum = FIRST_PAGE_NUM;
    }

    final Criteria criteria = new Criteria();
    if (!StringUtils.isEmpty(lastId)) {
      if (pageNum != FIRST_PAGE_NUM) {
        criteria.and(ID).gt(new ObjectId(lastId));
      }
      query.limit(pageSize);
    } else {
      int skip = pageSize * (pageNum - 1);
      query.skip(skip).limit(pageSize);
    }

    final List<T> entityList = mongoTemplate.find(query.addCriteria(criteria).with(new Sort(Lists.newArrayList(new Order(Direction.ASC, ID)))), entityClass);

    final MongoPageResult<R> pageResult = new MongoPageResult<>();
    pageResult.setTotal(total);
    pageResult.setPages(pages);
    pageResult.setPageSize(pageSize);
    pageResult.setPageNum(pageNum);
    pageResult.setList(entityList.stream().map(mapper).collect(Collectors.toList()));
    return pageResult;
  }
}
